package snmp

import (
	"errors"
	"time"

	g "github.com/gosnmp/gosnmp"
)

const (
	Version1  g.SnmpVersion = 0x0
	Version2c g.SnmpVersion = 0x1
	Version3  g.SnmpVersion = 0x3
)

var (
	port      = 161
	community = "public"
	version   = Version2c
	timeout   = time.Duration(2) * time.Second
)

type Snmp struct {
	Conn *g.GoSNMP
}

type SnmpServiceConfig struct {
	Port      int
	Community string
	Version   g.SnmpVersion
	Timeout   time.Duration
}

func NewSnmp(ip string, option ...SnmpServiceConfig) *Snmp {
	//option
	if len(option) > 0 {
		port = option[0].Port
		community = option[0].Community
		version = option[0].Version
		timeout = option[0].Timeout
	}

	s := new(Snmp)
	s.Conn = &g.GoSNMP{
		Target:    ip,
		Port:      uint16(port),
		Community: community,
		Version:   g.Version2c,
		Timeout:   timeout,
	}

	if err := s.Conn.Connect(); err != nil {
		panic("Connect() err:" + err.Error())
	}
	return s
}

//getter
func (s *Snmp) Get(oids []string) (result map[string]string, err error) {
	result = make(map[string]string)
	var res *g.SnmpPacket
	res, err = s.Conn.Get(oids)
	if err != nil {
		return
	}

	for _, v := range res.Variables {
		if str := filterSnmpValue(v); str != "" {
			result[v.Name] = str
		} else {
			err = errors.New("not support type")
		}
	}
	return
}

//walkAll
func (s *Snmp) Walk(oid string) (result map[string]string, err error) {
	result = make(map[string]string)
	var res []g.SnmpPDU
	res, err = s.Conn.WalkAll(oid)
	if err != nil {
		return
	}

	for _, v := range res {
		if str := filterSnmpValue(v); str != "" {
			result[v.Name] = str
		} else {
			err = errors.New("not support type")
		}
	}
	return
}

//close connection
func (s *Snmp) Close() {
	s.Conn.Conn.Close()
}

//实现snmpset
func (s *Snmp) Set(name string, v string) (string, string, error) {
	pdus := make([]g.SnmpPDU, 1)
	pdus[0] = g.SnmpPDU{
		Name:  name,
		Type:  g.OctetString,
		Value: v,
	}
	res, err := s.Conn.Set(pdus)
	if err != nil {
		return "", "", err
	}
	if str := filterSnmpValue(res.Variables[0]); str != "" {
		return res.Variables[0].Name, str, nil
	}
	return "", "", errors.New("not support type")
}

//实现snmpget
func (s *Snmp) GetOne(name string) (string, string, error) {
	res, err := s.Conn.Get([]string{name})
	if err != nil {
		return "", "", err
	}
	if len(res.Variables) == 0 {
		return "", "", nil
	}
	if str := filterSnmpValue(res.Variables[0]); str != "" {
		return res.Variables[0].Name, str, nil
	}
	return "", "", errors.New("not support type")
}
